
/*
 *
 *  * Copyright (c) 2016. For Intelligent Group.
 *
 */

package com.intelligent.ispc.chart.spc;

import com.intelligent.ispc.foundation.chart.AbstractChart;
import com.intelligent.ispc.foundation.chart.axis.FixedNumberAxis;
import com.intelligent.ispc.foundation.chart.data.xy.SimpleIntervalXYDataset;
import com.intelligent.ispc.foundation.chart.util.ChartColor;
import com.intelligent.ispc.foundation.chart.util.ChartUtil;
import com.intelligent.ispc.r.dto.DefaultPlotDataDto;
import com.intelligent.ispc.r.dto.NormalDistributionDto;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.labels.StandardXYToolTipGenerator;
import org.jfree.chart.plot.DatasetRenderingOrder;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.ValueMarker;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYBarRenderer;
import org.jfree.chart.renderer.xy.XYSplineRenderer;
import org.jfree.data.xy.IntervalXYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import org.jfree.ui.RectangleAnchor;
import org.jfree.ui.RectangleInsets;
import org.jfree.ui.TextAnchor;

import java.awt.*;
import java.text.DecimalFormat;
import java.text.NumberFormat;

/**
 * Created by Peter on 2016/8/4.
 * Modified by QiangChen on 2016/11/7.
 */
public class NormalDistributionChart extends AbstractChart {

    private IntervalXYDataset histogramDataset;
    private IntervalXYDataset curveDataset;

    private NormalDistributionDto sourceData;
    private JFreeChart localChart;

    private String charName;
    private boolean isDataValid;

    /**
     * @param normalDistributionDto is the original data of the drawing.
     */
    public NormalDistributionChart(NormalDistributionDto normalDistributionDto) {
        this.isDataValid = true;
        this.sourceData = normalDistributionDto;
        this.charName = "Normal Distribution Chart";
        this.histogramDataset = createHistogramDataset();
        this.curveDataset = createCurveDataset();
    }

    /**
     * @param chartName             is the name of the NormalDistributionChart.
     * @param normalDistributionDto is the original data of the drawing.
     */
    public NormalDistributionChart(String chartName, NormalDistributionDto normalDistributionDto) {
        this.isDataValid = true;
        this.sourceData = normalDistributionDto;
        this.charName = chartName;
        this.histogramDataset = createHistogramDataset();
        this.curveDataset = createCurveDataset();
    }

    @Override
    protected JFreeChart buildChart() {
        //Blank panel is returned when drawing data is invalid.
        if (!isDataValid) {
            return this.blankChart(charName);
        }

        localChart = ChartFactory.createXYBarChart(charName, "", false, "", histogramDataset, PlotOrientation.VERTICAL, false, true, false);
        XYPlot localXYPlot = (XYPlot) localChart.getPlot();
        XYBarRenderer barRenderer = new XYBarRenderer();
        barRenderer.setShadowVisible(false);
        barRenderer.setSeriesPaint(0, ChartColor.BLUE);
        barRenderer.setSeriesOutlinePaint(0, ChartColor.WHITE);
        barRenderer.setDrawBarOutline(true);
        barRenderer.setSeriesToolTipGenerator(0, new StandardXYToolTipGenerator("x={1}, y={2}", NumberFormat.getNumberInstance(), NumberFormat.getNumberInstance()));
        localXYPlot.setRenderer(barRenderer);

        NumberFormat nf = NumberFormat.getInstance();
        nf.setMaximumFractionDigits(this.getDigNum());
        nf.setGroupingUsed(false);

        XYSplineRenderer curveRenderer = new XYSplineRenderer();
        curveRenderer.setBaseShapesVisible(false);
        NumberAxis localNumberAxis1 = (NumberAxis) localXYPlot.getRangeAxis(0);
        localNumberAxis1.setNumberFormatOverride(nf);
        NumberAxis localNumberAxis2 = new NumberAxis("");
        localNumberAxis2.setNumberFormatOverride(nf);
        localXYPlot.setRangeAxis(1, localNumberAxis2);
        localXYPlot.setDataset(1, curveDataset);
        localXYPlot.setRenderer(1, curveRenderer);
        localXYPlot.mapDatasetToRangeAxis(1, 1);
        localXYPlot.setDatasetRenderingOrder(DatasetRenderingOrder.FORWARD);

        FixedNumberAxis rangeAxis1 = new FixedNumberAxis();
        rangeAxis1.setR(localXYPlot.getRangeAxis(0).getRange());
        rangeAxis1.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
        rangeAxis1.setTickLabelInsets(new RectangleInsets(15, 0, 15, 0));
        localXYPlot.setRangeAxis(0, rangeAxis1);

        FixedNumberAxis rangeAxis2 = new FixedNumberAxis();
        rangeAxis2.setNumberFormatOverride(nf);
        rangeAxis2.setR(localXYPlot.getRangeAxis(1).getRange());
        rangeAxis2.setTickLabelInsets(new RectangleInsets(15, 0, 15, 0));
        localXYPlot.setRangeAxis(1, rangeAxis2);

        FixedNumberAxis domainAxis = new FixedNumberAxis();
        domainAxis.setR(localXYPlot.getDomainAxis().getRange());
        localXYPlot.setDomainAxis(domainAxis);

        //Draw lcl、-2σ、-σ、c、σ、2σ、ucl line.
        double[] meanData = sourceData.getStatisticsDto().getClValue();
        Color[] colors = new Color[]{ChartColor.RED, ChartColor.BLUE, ChartColor.GREEN, ChartColor.GRAYBLUE, ChartColor.GREEN, ChartColor.BLUE, ChartColor.RED};
        String[] lineTitles = new String[]{"lcl=", "-2σ=", "-σ=", "c=", "σ=", "2σ=", "ucl="};
        ValueMarker valueMarker;
        for (int i = 0; i < meanData.length; i++) {
            valueMarker = ChartUtil.drawWarningLines(meanData[i], lineTitles[i] + new DecimalFormat(this.getDigNumFormat()).format(meanData[i]), colors[i]);
            valueMarker.setStroke(new BasicStroke(1.0F));
            valueMarker.setLabelAnchor(RectangleAnchor.TOP);
            valueMarker.setLabelTextAnchor(TextAnchor.TOP_CENTER);
            localXYPlot.addDomainMarker(valueMarker);
        }

        //Draw usl line
        if (sourceData.getStandardDto() != null && sourceData.getStandardDto().getUslValue() != null) {
            double usl = sourceData.getStandardDto().getUslValue();
            valueMarker = ChartUtil.drawWarningLines(usl, "usl=" + new DecimalFormat(this.getDigNumFormat()).format(usl), ChartColor.MAGENTA);
            valueMarker.setStroke(new BasicStroke(1.0F));
            valueMarker.setLabelAnchor(RectangleAnchor.TOP);
            valueMarker.setLabelTextAnchor(TextAnchor.TOP_CENTER);
            localXYPlot.addDomainMarker(valueMarker);
        }

        //Draw lsl line
        if (sourceData.getStandardDto() != null && sourceData.getStandardDto().getLslValue() != null) {
            double lsl = sourceData.getStandardDto().getLslValue();
            valueMarker = ChartUtil.drawWarningLines(lsl, "lsl=" + new DecimalFormat(this.getDigNumFormat()).format(lsl), ChartColor.MAGENTA);
            valueMarker.setStroke(new BasicStroke(1.0F));
            valueMarker.setLabelAnchor(RectangleAnchor.TOP);
            valueMarker.setLabelTextAnchor(TextAnchor.TOP_CENTER);
            localXYPlot.addDomainMarker(valueMarker);
        }
        return localChart;
    }

    /*
    * Create dataset for histogram chart, and the abnormal data is processed as necessary.
    * */
    private IntervalXYDataset createHistogramDataset() {
        DefaultPlotDataDto histogramDto = null;
        if (sourceData == null) {
            isDataValid = false;
        } else {
            histogramDto = sourceData.getHistogramDto();
        }
        if (histogramDto == null || histogramDto.getX() == null || histogramDto.getY() == null) {
            isDataValid = false;
        } else {
            double[] x = histogramDto.getX();
            double[] y = histogramDto.getY();
            double[] startX = null;
            double[] endX = null;
            if (x.length == 1) {
                startX = new double[x.length];
                endX = new double[x.length];
            } else {
                startX = new double[x.length - 1];
                endX = new double[x.length - 1];
            }
            SimpleIntervalXYDataset dataset = new SimpleIntervalXYDataset();
            for (int i = 0; i < x.length; i++) {
                if (i != x.length - 1) {
                    startX[i] = x[i];
                }
                if (i > 0) {
                    endX[i - 1] = x[i];
                }
            }

            dataset.setxStart(startX);
            dataset.setxEnd(endX);
            dataset.setyValues(y);

            return dataset;
        }
        return null;
    }

    /*
    * Create dataset for normal curve chart, and the abnormal data is processed as necessary.
    * */
    private IntervalXYDataset createCurveDataset() {
        DefaultPlotDataDto normalCurveDto = null;
        if (sourceData == null) {
            isDataValid = false;
        } else {
            normalCurveDto = sourceData.getNormalCurveDto();
        }
        if (normalCurveDto == null || normalCurveDto.getX() == null || normalCurveDto.getY() == null) {
            isDataValid = false;
        } else {
            double[] x = normalCurveDto.getX();
            double[] y = normalCurveDto.getY();

            XYSeries localXYSeries = new XYSeries("ND");
            for (int i = 0; i < x.length; i++) {
                if (Double.isNaN(x[i]) || Double.isNaN(y[i])) {
                    continue;
                }
                localXYSeries.add(x[i], y[i]);
            }
            XYSeriesCollection localXYSeriesCollection = new XYSeriesCollection(localXYSeries);
            return localXYSeriesCollection;
        }
        return null;
    }


    public NormalDistributionDto getSourceData() {
        return sourceData;
    }

    public JFreeChart getLocalChart() {
        return localChart;
    }

}

